﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using nl.tno.ict.multitouch.wpfmultitouch.gestures;
using System.Collections;
using System.IO;
using System.Threading;
using nl.tno.ict.multitouch.wpfmultitouch.events;
using TouchLibEvents;
using System.ComponentModel;

namespace nl.tno.ict.multitouch.wpfmultitouch.gestures
{   
    public class Gesture
    {
        private double _actualHeight = 0;
        private double _actualWidth = 0;
        private GeometricRecognizer _rec;
        private string[] _gesturesToLoad;
        public event RecognisedGestureHandler GestureHandler;
        public event DebugInfoHandler DebugHandler;

        private System.DateTime _d;
        private int _count = 0;
        private NBestList _firstResult = null;
        public Thread Thread;

        /// <summary>
        /// Get the current screensize width
        /// </summary>
        public double ScreenSizeWidth
        {
            set { this._actualWidth = value; }
        }

        /// <summary>
        /// Get the current screensize height
        /// </summary>
        public double ScreenSizeHeight
        {
            set { this._actualHeight = value; }
        }

        /// <summary>
        /// Constructor of Geasture class. 
        /// Load all available gestures which can check the current maked gesture.
        /// Starts a new threat to send a gesture if the wait time has elapsed.
        /// </summary>
        public Gesture()
        {
            try
            {
                _rec = new GeometricRecognizer();
                _gesturesToLoad = Directory.GetFiles(@"gesturemap\", "*.xml");
                this.loadAllGestures(_gesturesToLoad);
                Thread = new Thread(new ThreadStart(this.sendGesture));
                Thread.Start();
            }
            catch (IOException ex)
            {
                Console.WriteLine("Gesture directory not found!: " + ex);
            }
        }

        /// <summary>
        /// Load all gestures.
        /// </summary>
        /// <param name="gesturesToLoad">Array which contains all gesture xml files.</param>
        private void loadAllGestures(string[] gesturesToLoad)
        {
            foreach (string str in gesturesToLoad)
            {
                if(!_rec.LoadGesture(str))
                    Console.WriteLine("Error loading gesture: " + str);
            }
        }

        /// <summary>
        /// Gets all raw pointlists from the nl.tno.ict.multitouch.framework class and send them to a class which will check the gesture. 
        /// A list will be sended if it contains more than 5 elements.
        /// </summary>
        /// <param name="pointList">Raw pointlist form nl.tno.ict.multitouch.framework</param>
        public void PossibleGesturePointList(List<TuioMoveFingerEventArgs> pointList)
        {
            if (pointList.Count >= 5)
            {
                this.checkGesture(pointList);   
            }
        }

        /// <summary>
        /// Checks all incoming gesture(s) and send the gesture name if it will be recognised.
        /// </summary>
        /// <param name="pointList">Raw pointlist from nl.tno.ict.multitouch.framework</param>
        private void checkGesture(List<TuioMoveFingerEventArgs> pointList)
        {            
            if (_rec.NumGestures > 0)
            {
                NBestList result = _rec.Recognize(this.getAllPointsOfGesture(pointList));
                this.ThrowDebugEvent(result.Name + ", score: " + Math.Round(result.Score, 2) * 100 + "%");
                if (Math.Round(result.Score, 2) >= XMLSettingsReader.RECOGNISEPERCENTAGE)
                {
                    _count++;
                    if (_count == 1)
                    {
                        _firstResult = result;
                        _d = System.DateTime.Now.AddMilliseconds(500.0);
                    }
                    else if (_count == 2 && _d > System.DateTime.Now && _firstResult.Name == result.Name)
                    {
                        this.throwGestureEvent("Dual" + result.Name);
                        _count = 0;
                        _d = new DateTime();
                    }
                    else if (_count == 2 && (System.DateTime.Now > _d || _firstResult.Name != result.Name))
                    {
                        this.throwGestureEvent(_firstResult.Name);
                        _firstResult = result;
                        _count = 1;
                        _d = System.DateTime.Now.AddMilliseconds(500.0);
                    }
                }
            }
        }

        /// <summary>
        /// Returns a new arraylist which contains an ordered list of PointR-points.
        /// </summary>
        /// <param name="pointList">Raw pointlist from nl.tno.ict.multitouch.framework</param>
        /// <returns>Returns ordered list of PointR-points</returns>
        private ArrayList getAllPointsOfGesture(List<TuioMoveFingerEventArgs> pointList)
        {
            ArrayList list = new ArrayList(256);
            
            foreach (TuioMoveFingerEventArgs p in pointList)
            {
                list.Add(new PointR(Math.Round((double)p.Finger.CurrentPosition.X * _actualWidth), Math.Round((double)p.Finger.CurrentPosition.Y * _actualHeight), p.Finger.TickCount));
            }

            return this.orderedList(list);
        }

        /// <summary>
        /// This method will order the incoming list 
        /// </summary>
        /// <param name="list">List of PointR-points</param>
        /// <returns>Returns new ordered list of PointR-points</returns>
        private ArrayList orderedList(ArrayList list)
        {
            double lowest_Y = 1000.0;
            int position = 0;
            
            for (int i = 0; i < list.Count; i++)
            {
                if (((PointR)list[i]).Y < lowest_Y)
                {
                    lowest_Y = ((PointR)list[i]).Y;
                    position = i;
                }
            }

            ArrayList newlist = new ArrayList(256);

            for (int i = position; i < list.Count; i++)
            {
                newlist.Add(list[i]);
            }

            for (int i = 0; i < position; i++)
            {
                newlist.Add(list[i]);
            }
            
            return newlist;
        }

        /// <summary>
        /// Thread which take care the gesture will be sended if the wait time elapsed.
        /// </summary>
        private void sendGesture()
        {
            while (true)
            {                
                if (_d.CompareTo(System.DateTime.Now) == 1)
                {
                    this.throwGestureEvent(_firstResult.Name);
                    Console.WriteLine("Thread: Gesture Sended");
                    _count = 0;
                    _d = new DateTime(); 
                }
                Thread.Sleep(400);
            }
        }

        /// <summary>
        /// Throws an event with the recognised gesture name.
        /// </summary>
        /// <param name="gestureName">Name of the gesture</param>
        private void throwGestureEvent(string gestureName)
        {
            if (GestureHandler != null) GestureHandler(this, new RecognisedGestureEventArgs((PossibleGestures)new EnumConverter(typeof(PossibleGestures)).ConvertFromString(gestureName)));
        }

        /// <summary>
        /// Throws an event with the recognised gesture name and percentage.
        /// </summary>
        /// <param name="output">Recognised gesture name and percentage</param>
        public void ThrowDebugEvent(string output)
        {
            if (DebugHandler != null) DebugHandler(this, new DebugInfoEventArgs(output));
        }
    }
}
